package compile;

import Util.FileUtil;
import com.sun.media.jfxmediaimpl.HostUtils;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

//用这个类表示完整的编译运行的过程
public class Task {
    //此处罗列出需要的临时文件(用于进程间通信)文件名约定
    //所有的临时文件要放到这个目录中
    private String WORK_DIR;
    //要编译执行的类的名字,影响到源代码的文件名
    private String CLASS = "Solution";
    //要编译执行的文件名
    private String CODE;
    //程序标准输出放置的文件
    private String STDOUT;
    //程序标准错误放置的文件
    private String STDERR;
    //编译出错详细信息放置的文件
    private String COMPILE_ERROR;//用这个文件完成进程间通信
    public Task() {
        //先生成唯一的id,根据这个id来拼装出目录的名字,
        WORK_DIR ="./tmp/" + UUID.randomUUID().toString() +"/";
        // 然后生成后续的文件名
        //每次都会从产生一个新的包,存放用户数据,占用磁盘空间,记录日志
        //定期将服务器的用户数据导入备份服务器中
        CODE = WORK_DIR + CLASS + ".java";
        STDOUT = WORK_DIR + "stdout.txt";
        STDERR = WORK_DIR + "stderr.txt";
        COMPILE_ERROR = WORK_DIR + "compile_error.txt";


    }
    //Question  表示用户提交的代码
    //Answer    表示代码的编译运行结果
    public Answer compileAndRun(Question question) throws IOException, InterruptedException {
       Answer answer = new Answer();
        //0.给这些临时文件准备好一个目录
        //判定WORD_DIR是否存在,如果存在就跳过,不存在就创建
        File file = new File(WORK_DIR);
        if(!file.exists()) {
            //创建对应的目录
            file.mkdirs();
        }

        //1.先准备好需要用到的临时文件
        //  要编译的源代码文件
        //  编译出错要放进一个文件
        //  最终运行的标准输出标准错误也要放到文件中
       FileUtil.writeFile(CODE,question.getCode());

        //2.构造编译指令(javac),并进行执行,预期得到的结果
        //  就是一个对应的.class文件,以及编译出错的文件
        //  -d表示生成的.class文件放置的位置
        //  javac -encoding utf-8 ./tmp/Solution.java -d ./tmp
        //  String.format 类似C中的sprintf
       // String compileCmd = "javac -encoding utf-8" + CODE +" -d"+ WORK_DIR;
        // encoding=utf-8意思是编码格式为UTF-8格式。
        String compileCmd = String.format("javac -encoding utf-8 %s -d %s",
                CODE,WORK_DIR);
        System.out.println("编译命令:"+compileCmd);
        //创建子进程进行编译,此处不关心javac的标准输出,因为没输出什么东西
        //只关心javac的标准错误,标准错误中就包含了编译出错的信息
        CommandUtil.run(compileCmd,null,COMPILE_ERROR);
        //此处判断编译是否出错,看COMPILE_ERROR是不是空的
        String compileError = FileUtil.readFile(COMPILE_ERROR);
        if (!compileError.equals("")){
            //说明编译出错
            answer.setErrno(1);
            answer.setReason(compileError);
            return answer;
        }

        //3.构造运行指令(java),并进行执行,预期得到的结果
        //  就是这个代码的标准输出的文件和标准错误的文件
        //  为了让java命令找到.class文件的位置,还需要加上一个选项
        //  -classpath 通过这个选项来执行 .class文件放到哪个目录里了
        //  java Solution
        String runCmd = String.format("java -classpath %s %s",WORK_DIR,CLASS);
        System.out.println("runCmd: "+runCmd);
        CommandUtil.run(runCmd,STDOUT,STDERR);
        // 尝试读取STDERR中的内容,不为空,运行出错
        //如果程序抛出异常,异常调用栈信息就在STDERR中
        String runError = FileUtil.readFile(STDERR);
        if(!runError.equals("")){
            //运行出错
            answer.setErrno(2);
            answer.setReason(runError);
            return answer;
        }
        //4.把最终结果构造成Answer对象,并返回
        answer.setErrno(0);
        String runStdout = FileUtil.readFile(STDOUT);
        answer.setStdout(runStdout);
        return answer;
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        Task task = new Task();
        Question question = new Question();
        question.setCode("public class Solution {\n" +
                "    public static void main(String[] args) {\n" +
                "        System.out.println(\"stdout\");\n" +
                "        String s = null;\n" +
                "        System.out.println(s.length());\n" +
                "    }\n" +
                "}");
        Answer answer = task.compileAndRun(question);
        System.out.println(answer);

    }
}
